using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using System.Diagnostics;
using System.IO;

namespace DrawImageSample
{
    public partial class DrawImageSample : Form
    {
        private List<Screen> mScreens;
        private int mCurrentScreen;
        private Pen mPen;
        private Stroke mCurrentStroke;

        public DrawImageSample()
        {
            InitializeComponent();

            //this.DoubleBuffered = true;

            this.mCurrentScreen = 0;
            this.mScreens = new List<Screen>();
            Screen screen = new Screen(0);
            this.mScreens.Add(screen);
            this.panImageList.Controls.Add(screen.Thumbnail);
            this.mPen = Resources.GetPen(this.btnPenColor.BackColor, Convert.ToSingle(this.numPenSize.Value));
            this.mCurrentStroke = new Stroke(this.mPen.Color, this.mPen.Width);
            ScreenThumbnail.SelectionChanged += new EventHandler(ScreenThumbnail_SelectionChanged);
            screen.Select();

            this.panCanvas.Width = Variables.CANVAS_WIDTH;
            this.panCanvas.Height = Variables.CANVAS_HEIGHT;
        }

        void ScreenThumbnail_SelectionChanged(object sender, EventArgs e)
        {
            foreach (Screen screen in this.mScreens)
            {
                if (screen.Thumbnail == sender as ScreenThumbnail)
                {
                    this.mCurrentScreen = screen.ID;
                    this.UpdateCanvas();
                    this.UpdateImageList();
                    break;
                }
            }
        }

        private void panCanvas_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                using (Graphics graphics = Resources.GetGraphics(this.panCanvas.CreateGraphics()))
                {
                    Point lastPoint = this.mCurrentStroke.Points[this.mCurrentStroke.Points.Count - 1];
                    graphics.DrawLine(this.mPen, lastPoint, e.Location);
                }
            
                this.mCurrentStroke.Points.Add(e.Location);
            }
        }

        private void panCanvas_MouseDown(object sender, MouseEventArgs e)
        {
            this.mCurrentStroke = new Stroke(this.mPen.Color, this.mPen.Width);
            this.mCurrentStroke.Points.Add(e.Location);
        }

        private void panCanvas_MouseUp(object sender, MouseEventArgs e)
        {
            float delta = 0.001f;
            using (Graphics graphics = Resources.GetGraphics(this.panCanvas.CreateGraphics()))
            {
                Point lastPoint = this.mCurrentStroke.Points[this.mCurrentStroke.Points.Count - 1];
                graphics.DrawLine(this.mPen, lastPoint.X + 0.0f, lastPoint.Y + 0.0f, e.X + delta, e.Y + delta);
            }
            this.mCurrentStroke.Points.Add(e.Location);
            this.mCurrentStroke.BoundingBox = GetBoundingBox(this.mCurrentStroke.Points);
            this.mScreens[this.mCurrentScreen].AddStroke(this.mCurrentStroke);
            this.UpdateImageList();
        }

        private Rectangle GetBoundingBox(List<Point> pPoints)
        {
            if (pPoints.Count == 0)
            {
                throw new Exception();
            }

            int left, right, top, bottom;
            Point firstPoint = pPoints[0];
            left = right = firstPoint.X;
            top = bottom = firstPoint.Y;

            foreach (Point point in pPoints)
            {
                left = Math.Min(left, point.X);
                right = Math.Max(right, point.X);

                top = Math.Min(top, point.Y);
                bottom = Math.Max(bottom, point.Y);
            }

            return new Rectangle(left, top, right - left, bottom - top);
        }

        private void btnRefresh_Click(object sender, EventArgs e)
        {
            // refresh
            this.UpdateCanvas();
        }

        private void btnNewSlide_Click(object sender, EventArgs e)
        {
            // create new slide
            this.mCurrentScreen = this.mScreens.Count;
            Screen screen = new Screen(this.mScreens.Count);
            this.mScreens.Add(screen);
            this.panImageList.Controls.Add(screen.Thumbnail);
            this.lblSlideCount.Text = String.Format("{0} / {1}", this.mScreens.Count, this.mScreens.Count);
            screen.Select();
        }


        private void btnCloneSlide_Click(object sender, EventArgs e)
        {
            // clone current slide
            Screen screen = new Screen(this.mScreens[this.mCurrentScreen], this.mScreens.Count);
            this.mScreens.Add(screen);
            this.panImageList.Controls.Add(screen.Thumbnail);
            this.mCurrentScreen = this.mScreens.Count - 1;
            this.lblSlideCount.Text = String.Format("{0} / {1}", this.mScreens.Count, this.mScreens.Count);
            screen.Select();
        }

        private void btnRemoveSlide_Click(object sender, EventArgs e)
        {
            // remove the current screen
            if (this.mScreens.Count > 1)
            {
                for (int i = this.mScreens.Count - 1; i > this.mCurrentScreen; i--)
                {
                    this.mScreens[i].ID--;
                }
                this.panImageList.Controls.Remove(this.mScreens[this.mCurrentScreen].Thumbnail);
                this.mScreens.Remove(this.mScreens[this.mCurrentScreen]);

                if (this.mCurrentScreen >= this.mScreens.Count)
                {
                    this.mCurrentScreen--;
                }
                this.mScreens[this.mCurrentScreen].Select();
                this.UpdateCanvas();
            }
        }

        private void btnClearSlide_Click(object sender, EventArgs e)
        {
            // clear current slide
            this.mScreens[this.mCurrentScreen].Clear();
            this.UpdateCanvas();
        }

        private void btnSaveSlide_Click(object sender, EventArgs e)
        {
            // save slide
            foreach (Screen screen in this.mScreens)
            {
                screen.Save();
            }
        }

        private void btnPenColor_Click(object sender, EventArgs e)
        {
            // show the color picker
            ColorDialog colorDlg = new ColorDialog();
            colorDlg.Color = this.mPen.Color;
            if (colorDlg.ShowDialog() == DialogResult.OK)
            {
                this.mPen = Resources.GetPen(colorDlg.Color, this.mPen.Width);
                this.btnPenColor.BackColor = colorDlg.Color;
            }
        }

        private void numPenSize_ValueChanged(object sender, EventArgs e)
        {
            this.mPen = Resources.GetPen(this.mPen.Color, Convert.ToSingle(this.numPenSize.Value));
        }

        private void panCanvas_Paint(object sender, PaintEventArgs e)
        {
            //using (Bitmap bitmap = new Bitmap(panCanvas.Width, panCanvas.Height))
            {
                if (e.ClipRectangle == this.panCanvas.ClientRectangle)
                {
                    Console.WriteLine("THE WHOLE CANVAS WAS PAINTED!");
                }
                else
                {
                    Console.WriteLine(e.ClipRectangle.ToString() + " WAS PAINTED");
                }
                e.Graphics.SetClip(e.ClipRectangle);

                //Graphics bitmapGraphics = Resources.GetGraphics(Graphics.FromImage(bitmap));
                this.mScreens[this.mCurrentScreen].Draw(Resources.GetGraphics(e.Graphics), 1.0f);
                //e.Graphics.DrawImageUnscaled(bitmap, 0, 0);
            }
        }

        private void panImage_MouseDown(object sender, MouseEventArgs e)
        {
            // select something
            if ((e.X > Variables.THUMBNAIL_BORDER) &&
                (e.X < (Variables.THUMBNAIL_WIDTH + Variables.THUMBNAIL_BORDER + Variables.THUMBNAIL_SPACE)))
            {
                // calculate the Y position
                for (int i = 0; i < this.mScreens.Count; i++)
                {
                    Rectangle expectedRect = this.GetThumbnailRect(i);
                    if (expectedRect.Contains(e.Location))
                    {
                        this.mCurrentScreen = i;
                        this.UpdateCanvas();
                        this.lblSlideCount.Text = String.Format("{0} / {1}", this.mCurrentScreen + 1, this.mScreens.Count);
                        break;
                    }
                }
            }
        }

        private void UpdateCanvas()
        {
            this.panCanvas.Invalidate();
            this.UpdateImageList();
        }

        private void UpdateImageList()
        {
            using (Graphics graphics = Resources.GetGraphics(Graphics.FromImage(this.mScreens[this.mCurrentScreen].Thumbnail.Image)))
            {
                graphics.Clear(Color.White);
                this.mScreens[this.mCurrentScreen].Draw(graphics, (Variables.THUMBNAIL_WIDTH / (float)Variables.CANVAS_WIDTH));
            }
            this.panImageList.ScrollControlIntoView(this.mScreens[this.mCurrentScreen].Thumbnail);
            this.mScreens[this.mCurrentScreen].Thumbnail.Refresh();
        }

        private Rectangle GetThumbnailRect(int thumbnailIndex)
        {
            // compute x
            int x = (Variables.THUMBNAIL_SPACE          // left margin
                + Variables.THUMBNAIL_BORDER);          // left border

            // compute y
            int y = (Variables.THUMBNAIL_SPACE          // first margin
                    + Variables.THUMBNAIL_BORDER)       // first border
                    + ((Variables.THUMBNAIL_SPACE       // space between thumbnails
                    + Variables.THUMBNAIL_BORDER        // top border
                    + Variables.THUMBNAIL_HEIGHT        // height of thumbnail
                    + Variables.THUMBNAIL_BORDER)       // bottom border
                    * thumbnailIndex);                  // thumbnail index

            // return the rectangle
            return new Rectangle(x, y, Variables.THUMBNAIL_WIDTH, Variables.THUMBNAIL_HEIGHT);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            this.panCanvas.Invalidate(this.mCurrentStroke.BoundingBox);
        }

        private void btnLoad_Click(object sender, EventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.Title = "Open Project";
            dlg.Filter = "WOZ Project File|*.woz|All files|*.*";
            dlg.FilterIndex = 0;
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                using (StreamReader tr = new StreamReader(dlg.FileName))
                {
                    int numScreens = Convert.ToInt32(tr.ReadLine());
                    this.mScreens.Clear();
                    this.panImageList.Controls.Clear();
                    this.mCurrentScreen = 0;
                    Screen screen;
                    for (int i = 0; i < numScreens; i++)
                    {
                        screen = new Screen(i);
                        screen.ReadFromFile(tr);
                        this.mScreens.Add(screen);
                        this.panImageList.Controls.Add(screen.Thumbnail);
                        
                        using (Graphics graphics = Resources.GetGraphics(Graphics.FromImage(screen.Thumbnail.Image)))
                        {
                            graphics.Clear(Color.White);
                            screen.Draw(graphics, (Variables.THUMBNAIL_WIDTH / (float)Variables.CANVAS_WIDTH));
                        }
                        screen.Thumbnail.Refresh();
                    }
                }
                this.mScreens[this.mCurrentScreen].Select();
            }
        }

        private void btnSave_Click(object sender, EventArgs e)
        {
            SaveFileDialog dlg = new SaveFileDialog();
            dlg.Title = "Save Project";
            dlg.Filter = "WOZ Project File|*.woz|All files|*.*";
            dlg.FilterIndex = 0;
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                using (StreamWriter tw = new StreamWriter(dlg.FileName))
                {
                    tw.WriteLine(this.mScreens.Count);
                    foreach (Screen screen in this.mScreens)
                    {
                        screen.WriteToFile(tw);
                    }
                }
            }
        }
    }
}